// Copyright (c) 2002, 2003 Cunningham & Cunningham, Inc.
// Released under the terms of the GNU General Public License version 2 or later.

using System;
using System.Reflection;

namespace fit
{
	public class ActionFixture: Fixture
	{
		protected Parse cells;
		public static Fixture actor;

		// Traversal ////////////////////////////////

		public override void doCells(Parse cells)
		{
			this.cells = cells;
			try
			{
				string actionName = camel(cells.text());
				MethodInfo action = GetType().GetMethod(actionName, new Type[] {});
				action.Invoke(this, new Type[] {});
			}
			catch (Exception e)
			{
				exception(cells, e);
			}
		}

		// Actions //////////////////////////////////

		public virtual void start()
		{
			string actorText = cells.more.text();
			actor = loadFixture(actorText);
		}

		public virtual void enter()
		{
			MethodInfo method = findMethod(1);
			BaseEnter(method);
		}

		public virtual void enter(string prefix)
		{
			MethodInfo method = findMethod(1, prefix);
			BaseEnter(method);
		}

		void BaseEnter(MethodInfo method)
		{
			Type type = method.GetParameters()[0].ParameterType;
			String text = cells.more.more.text();
			cells.more.more.body = escape(text);
			dictionary[method.Name] = text;
			Object[] enterArgs = {TypeAdapter.on(actor, type).parse(text)};
			method.Invoke(actor, enterArgs);
		}

		public virtual void press()
		{
			MethodInfo methodInfo = findMethod(0);
			BasePress(methodInfo);
		}

		public virtual void press(string prefix)
		{
			MethodInfo methodInfo = findMethod(0, prefix);
			BasePress(methodInfo);
		}

		static void BasePress(MethodInfo methodInfo)
		{
			methodInfo.Invoke(actor, new Type[] {});
		}

		public virtual void check()
		{
			MethodInfo methodInfo = findMethod(0);
			BaseCheck(methodInfo);
		}

		public virtual void checkWithParam()
		{
			checkWithParam(string.Empty);
		}

		public virtual void checkWithParam(string prefix)
		{
			MethodInfo method = findMethod(1, prefix);
			TypeAdapter adapter = TypeAdapter.on(actor, method);
			Type type = method.GetParameters()[0].ParameterType;
			String paramStr = cells.more.more.text();
			cells.more.more.body = escape(paramStr);
//			dictionary[method.Name] = text;
			Object[] checkParams = {TypeAdapter.on(actor, type).parse(paramStr)};
			check(cells.more.more.more, adapter, checkParams);
		}

		public virtual void check(string prefix)
		{
			MethodInfo methodInfo = findMethod(0, prefix);
			BaseCheck(methodInfo);
		}

		void BaseCheck(MethodInfo methodInfo)
		{
			TypeAdapter adapter = TypeAdapter.on(actor, methodInfo);
			check(cells.more.more, adapter);
		}

		// Utility //////////////////////////////////

		protected virtual MethodInfo findMethod(int args)
		{
			string nextCellContent = cells.more.text();
			string expectedName = camel(nextCellContent);
			return findMethod(expectedName, args);
		}

		protected virtual MethodInfo findMethod(int numberOfArgs, string prefix)
		{
			string nextCellContent = cells.more.text();
			string expectedName = camel(string.Format("{0} {1}", prefix, nextCellContent));
			return findMethod(expectedName, numberOfArgs);
		}

		protected virtual MethodInfo findMethod(String expectedName, int numberOfArgs)
		{
			Type fixture = actor.GetType();
			MethodInfo[] methodList = fixture.GetMethods();
			MethodInfo result = null;
			for (int i = 0; i < methodList.Length; i++)
			{
				MethodInfo m = methodList[i];
				if (m.Name.ToLower().Equals(expectedName.ToLower()) && m.GetParameters().Length == numberOfArgs)
				{
					if (result == null)
					{
						result = m;
					}
					else
					{
						throw new MissingMethodException("Too many implementations of '" + expectedName + "' in '" + fixture + "'");
					}
				}
			}
			if (result == null)
			{
				throw new MissingMethodException("Could not find '" + expectedName + "' in '" + fixture + "'");
			}
			return result;
		}
	}
}